﻿// ------------------------------------------------------------------------
// 项目名称：Canroc.Net 
// 版权归属：Canroc（https://gitee.com/canroc）
//
// 许可证信息
// Canroc.Net项目主要遵循 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-APACHE 文件。
//
// 使用条款：
// 1.使用本项目应遵守相关法律法规和许可证的要求。
// 2.不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动。
// 3.任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任
//
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
//
// 其他重要信息
// Canroc.Net 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// ------------------------------------------------------------------------

using Canroc.Net.Core.Helper;
using Canroc.Net.Core.Options;
using Canroc.Net.RemoteHttp.WeChatPay;
using Furion.LinqBuilder;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net;

namespace Canroc.Net.TaskScheduler.System;

/// <summary>
///     同步微信支付平台证书
/// </summary>
[JobDetail("syncWeChatPayCertificate", Description = "同步微信支付平台证书", GroupName = "system", Concurrent = false)]
[Daily(TriggerId = "syncWeChatCertificateTrigger", Description = "每日执行", RunOnStart = false, StartNow = false)]
public class SyncWeChatPayCertificateJob : IJob
{
    /// <summary>
    ///     异步执行
    /// </summary>
    /// <param name="context"></param>
    /// <param name="stoppingToken"></param>
    /// <returns></returns>
    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
    {
        var weChatPayClient = App.GetRequiredService<IWeChatPayRemoteHttp>();
        var weChatPayCertificatesRep =
            App.GetRequiredService<SqlSugarRepository<SysWeChatPayCertificates>>();

        // 没有配置微信支付商户号，视为不启用微信支付，所以同步微信支付平台证书任务不执行
        var weChatPayOptions = App.GetOptions<WeChatPayOptions>();
        if (weChatPayOptions.MchId.IsNullOrEmpty())
        {
            return;
        }

        if (weChatPayOptions.RSAPrivateKey is null)
        {
            throw Oops.Oh("微信支付证书未找到，无法继续请求！");
        }

        var response = await weChatPayClient.GetCertificatesAsync();
        if (response.StatusCode != HttpStatusCode.OK)
        {
            using StreamReader stream =
                new(await response.ResponseMessage.Content.ReadAsStreamAsync(stoppingToken));
            var error = JsonConvert.DeserializeObject(await stream.ReadToEndAsync(stoppingToken)) as JObject;
            throw Oops.Oh("微信支付证书获取失败！").WithData(error ?? new JObject());
        }

        if (response.Result is null)
        {
            throw Oops.Oh("微信支付证书返回内容未找到");
        }

        var certificates = response.Result.Data;

        // 如果证书数组数量大于1，说明证书有更新，需要找出最新的证书保存到数据库
        var certificate =
            certificates.Count > 1
                ? certificates.OrderByDescending(w => w.ExpireTime).First()
                : certificates.First();

        // 如果获取的证书编号与系统的证书编号不一致，说明证书有更新，需要重新保存到数据库
        var weiXinPayCertificates =
            await weChatPayCertificatesRep.GetFirstAsync(w => w.SerialNo == certificate.SerialNo, stoppingToken);
        if (weiXinPayCertificates is null)
        {
            // 将现有的证书库中的启用证书设置为删除
            _ = await weChatPayCertificatesRep.AsUpdateable().SetColumns(w => w.IsDelete).Where(w => !w.IsDelete)
                .ExecuteCommandAsync(stoppingToken);

            // 将新证书信息保存数据库
            weiXinPayCertificates = new SysWeChatPayCertificates
            {
                SerialNo = certificate.SerialNo,
                EffectiveTime = certificate.EffectiveTime,
                ExpireTime = certificate.ExpireTime,
                Algorithm = certificate.EncryptCertificate.Algorithm,
                Nonce = certificate.EncryptCertificate.Nonce,
                AssociatedData = certificate.EncryptCertificate.AssociatedData,
                Ciphertext = certificate.EncryptCertificate.Ciphertext
            };
            _ = await weChatPayCertificatesRep.InsertAsync(weiXinPayCertificates, stoppingToken);

            // 解析证书并保存物理位置
            var weChatPayPublicKey = WeChatPayHelper.AEAD_AES_256_GCMDecryption(weChatPayOptions.ApiV3Key,
                weiXinPayCertificates.Ciphertext, weiXinPayCertificates.Nonce,
                weiXinPayCertificates.AssociatedData);
            // 将证书内容保存到本地
            var certPath = Path.Combine(AppContext.BaseDirectory, weChatPayOptions.CertificatePath);
            if (!Directory.Exists(certPath))
            {
                Directory.CreateDirectory(certPath);
            }

            var certFile = Path.Combine(certPath, $"{certificate.SerialNo}.pem");
            await File.WriteAllTextAsync(certFile, weChatPayPublicKey, stoppingToken);
        }
    }
}